package com.yanx.framework.aspect;

import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Slf4j
@Aspect
@Component
public class LogAspect {
    @Autowired
    private HttpServletRequest request;

//    private final static String SPACE = "\r\n                                                                                                     ";

    /**
     * 使用ThreadLocal创建对象，防止出现线程安全问题
     */
    private static final ThreadLocal<Long> START_TIME = ThreadLocal.withInitial(() -> 0L);

    /**
     * 定义请求日志切入点
     */
    @Pointcut(value = "@annotation(operation)")
    public void serviceStatistics(Operation operation) {

    }

    /**
     * 前置通知
     *
     * @param joinPoint
     * @param operation
     */
    @Before(value = "serviceStatistics(operation)")
    private void doBefore(JoinPoint joinPoint, Operation operation) {
        START_TIME.set(System.currentTimeMillis());
        String classPath = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        log.info(">>>  [{}] ({}) {}.{}{}",
                operation.summary(),
                request.getRequestURL(),
                classPath,
                methodName,
                args
        );
    }

    /**
     * 返回通知
     *
     * @param operation
     * @param returnValue
     */
    @AfterReturning(value = "serviceStatistics(operation)", returning = "returnValue")
    private void doAfterReturning(Operation operation, Object returnValue) {
        Object rtnObj = returnValue;
        if (returnValue instanceof List) {
            rtnObj = "返回List长度：" + ((List) returnValue).size();
        } else if (returnValue != null && returnValue.toString().length() > 100) {
            rtnObj = returnValue.toString().substring(0, 100) + "......";
        }
        log.info("<<<  [{}] ({}ms) {}",
                operation.summary(),
                getDuration(),
                rtnObj);
    }

    /**
     * 异常通知
     *
     * @param operation
     * @param e
     */
    @AfterThrowing(value = "serviceStatistics(operation)", throwing = "e")
    private void doAfterThrowing(Operation operation, Throwable e) {
        log.info("!!!  [{}] ({}ms) errormsg: {}",
                operation.summary(),
                getDuration(),
                e.getMessage());
    }

    private long getDuration() {
        long duration = System.currentTimeMillis() - START_TIME.get();
        START_TIME.remove();
        return duration;
    }
}
